#include "StdAfx.h"
#include <MyFC/TextControl.h>
#include <MyFC/Utility.h>

namespace My {

// InplaceEdit

String InplaceEdit::GetText()
{
	CString s;
	GetWindowText(s);
	return static_cast<LPCTSTR>(s);
}
	
void InplaceEdit::SetText(const String & s)
{
	SetWindowText(s.c_str());
}


TextControl::TextControl()
{
	m_pEdit = 0;
	m_hCurHandClick = AfxGetApp()->LoadCursor("CURSORHANDCLICK");
	m_Preferred = CSize(-1, -1);
	m_ReadOnly = false;
}

TextControl::~TextControl()
{
	if (m_pEdit != 0)
		delete m_pEdit;
}

void TextControl::SetReadOnly(bool on)
{
	if (on != m_ReadOnly) {
		m_ReadOnly = on;
		Invalidate();
	}
}

void TextControl::Apply()
{
	if (m_pEdit != 0) 
	{
		try {
			String s = m_pEdit->GetText();
			CloseInplaceEdit();
			SetText(s);
		} catch(...) {
			Cancel();
		}
	}
}

void TextControl::Cancel()
{
	CloseInplaceEdit();
}

void TextControl::OpenInplaceEdit()
{
	if (m_pEdit != 0)
		return;
	TRACE0("TextControl::OpenInplaceEdit()\n");

	m_pEdit = new InplaceEdit(* this);
	Add(m_pEdit);

	m_pEdit->Create(WS_CHILD|WS_VISIBLE|ES_AUTOHSCROLL|ES_LEFT, 
		CRect(), My::Control::GetWindow(), 0);
	const CFont * pFont = Font();
	if (pFont != 0)
		m_pEdit->SetFont(const_cast<CFont *>(pFont));

	m_pEdit->SetMargins(0, 0);
	String s;
	if (GetText(s)) 
	{
		m_pEdit->SetWindowText(s.c_str());
		m_pEdit->SetSel(0, -1, TRUE);
	}
	m_pEdit->SetFocus();

	UpdatePreferredSize();
	LayoutControls();
	Invalidate();
}

void TextControl::CloseInplaceEdit()
{
	if (m_pEdit == 0)
		return;

	TRACE0("TextControl::CloseInplaceEdit()\n");
	Remove(m_pEdit);
	delete m_pEdit;
	m_pEdit = 0;

	UpdatePreferredSize();
	Invalidate();
}

CRect TextControl::CalcTextRect(const String & text)
{
	My::ClientDC dc(this);
	My::SaveObjects save(&dc);

	CRect rClient = ClientRect();

	CRect r(rClient);
	r.InflateRect(-4, 0, -4, 0);

	if (r.IsRectEmpty())
		return r;

	SelectFont(&dc);
	const char * szText = text.empty() ? "   " : text.c_str();

	dc.DrawText(szText, &r, DT_SINGLELINE|DT_LEFT|DT_CALCRECT);
	r.OffsetRect(0, (rClient.Height() - r.Height()) / 2);

	CRect intersection;
	intersection.IntersectRect(&r, &rClient);
	m_TightBounds = (intersection != r) != 0;

	return intersection;
}

void TextControl::HandlePaint(CDC * pDC, const CRect & r)
{
	if (m_pEdit != 0)
		return;

	My::SaveObjects save(pDC);

	COLORREF cB = GetSysColor(COLOR_WINDOWTEXT);
	COLORREF cS = GetSysColor(COLOR_BTNSHADOW);
	COLORREF cBlue = RGB(0, 0, 128);

	COLORREF cOld = pDC->GetTextColor();
	String s;
	if (!GetText(s))
		s = "";
	if (!ControlEnabled(this))
		pDC->SetTextColor(cS);
	else if (ReadOnly()) 
		pDC->SetTextColor(cB);
	else
		pDC->SetTextColor(cBlue);

	m_TextRect = CalcTextRect(s);

	if (!m_TextRect.IsRectEmpty())
	{
		pDC->DrawText(s.c_str(), &m_TextRect, DT_SINGLELINE|DT_LEFT|DT_VCENTER|DT_END_ELLIPSIS);

		if (ControlEnabled(this) && !ReadOnly()) {
			DrawAlternateHorizLine(pDC, 
				m_TextRect.bottom, m_TextRect.left, m_TextRect.right + 1, cBlue);
		}
	}

	pDC->SetTextColor(cOld);
}

void TextControl::HandleLeftDown(unsigned int flags, const CPoint & p)
{
	Control::HandleLeftDown(flags, p);
	if (!ControlEnabled(this) || ReadOnly())
		return;
	if (m_pEdit == 0 && m_TextRect.PtInRect(p))
		OpenInplaceEdit();
	else
		if (Parent() != 0)
			Parent()->HandleLeftDown(flags, p);//Apply();
}

bool TextControl::HandleCursor(unsigned int flags, const CPoint & p)
{
	if (m_TextRect.PtInRect(p) && m_pEdit == 0 && ControlEnabled(this) && !ReadOnly())
		SetCursor(m_hCurHandClick);
	else
		SetCursor(::LoadCursor(NULL, IDC_ARROW));
	return true;
}

void TextControl::LayoutControls()
{
	if (m_pEdit != 0) 
	{
		CRect rClient = ClientRect();

		CString s;
		m_pEdit->GetWindowText(s);

		CRect rEdit = CalcTextRect(static_cast<const char *>(s));

		rEdit.right += 8;
		rEdit.InflateRect(3, 2);

		rEdit.IntersectRect(&rClient, &rEdit);

		if (rEdit.Width() < 8) {
			m_pEdit->ShowWindow(SW_HIDE);
		} else {
			m_pEdit->ShowWindow(SW_SHOW);
			m_pEdit->SetBounds(rEdit);
			m_pEdit->SetFocus();
		}
	}
}

void TextControl::HandleVisible(bool on)
{
	if (!on)
		Cancel();
}

void TextControl::HandleEnable(bool on)
{
	if (!on)
		Cancel();
}

CSize TextControl::GetPreferredSize() const 
{ 
//	if (m_Preferred == CSize(-1, -1))
		const_cast<TextControl *>(this)->UpdatePreferredSize();
	return m_Preferred; 
}

void TextControl::UpdatePreferredSize()
{
	String text;
	if (m_pEdit != 0) {
		CString s;
		m_pEdit->GetWindowText(s);
		text = s;
	} else
		GetText(text);
	My::ClientDC dc(this);
	My::SaveObjects save(&dc);

	if (text.empty())
		text = " ";
	SelectFont(&dc);
	CSize s = dc.GetTextExtent(text.c_str(), text.size());
	if (m_pEdit != 0) {
		s.cx += 8;
		s.cx += 6;
		s.cy += 4;
	} else {
		s.cx += 8;
		s.cy += 4;
	}
	SetPreferredSize(s);
}

void TextControl::SetPreferredSize(CSize s)
{
	if (s != m_Preferred) {
		m_Preferred = s;
		if (Parent() != 0)
			Parent()->LayoutControls();
	}
}

void TextControl::HandleFont()
{
	UpdatePreferredSize();
}

String TextControl::Hint() const
{
	if (!m_TightBounds || m_pEdit != 0)
		return String();
	String s;
	const_cast<TextControl *>(this)->GetText(s); //why GetText(..) is not a const method?
	return s;
}

// InplaceEdit

BEGIN_MESSAGE_MAP(InplaceEdit, Window<CEdit>)
	ON_WM_KEYDOWN()
	ON_WM_KILLFOCUS()
	ON_CONTROL_REFLECT(EN_UPDATE, OnChange)
END_MESSAGE_MAP()

InplaceEdit::InplaceEdit(TextControl & t) :
	m_Owner(t)
{
}

InplaceEdit::~InplaceEdit()
{
}

BOOL InplaceEdit::PreCreateWindow(CREATESTRUCT& cs)
{
	if (!CEdit::PreCreateWindow(cs))
		return FALSE;
	cs.dwExStyle |= WS_EX_CLIENTEDGE;
	return TRUE;
}

void InplaceEdit::OnKillFocus(CWnd* pNewWnd)
{
	if (IsWindowVisible())
		m_Owner.Apply();
}

void InplaceEdit::OnChange()
{
	m_Owner.UpdatePreferredSize();
	m_Owner.LayoutControls();
	m_Owner.Invalidate();
}

void InplaceEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	if (nChar == VK_RETURN)	{
		m_Owner.Apply();
		return;
	}
	if (nChar == VK_ESCAPE)	{
		m_Owner.Cancel();
		return;
	}
	CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
}

void InplaceEdit::SetFont(CFont * pFont)
{
	Widget::SetFont(pFont);
	CEdit::SetFont(pFont);
}

} // My
